﻿using Microsoft.EntityFrameworkCore;
using System;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using VisionCloud.Core.Model.Enums;
using VisionCloud.Core.Model.Ms;
using VisionCloud.Domain.Common;
using VisionCloud.Domain.File.Model;
using VisionCloud.Domain.Repository.Interface.Ms;
using VisionCloud.Domain.UnitOfWork;
using VisionCloud.Utility;
using VisionCloud.Utility.Redis;

namespace VisionSurface.Service.Sys
{
    public class UploadFileService
    {
        private readonly string UPLOAD_DIR = $"{ConfigHelper.GetValue<string>("TaskDotCloudData")}/DataFile";
        private readonly string TEMP_DIR = $"{ConfigHelper.GetValue<string>("TaskDotCloudData")}/UploadTemp";

        private const string redisPrex = "Applet:FileUpload";
        public IDotCloudUploadRepository DotCloudUploadRepository { get; set; }
        public IMsTaskRepository MsTaskRepository { get; set; }

        public IMsProjectRepository MsProjectRepository { get; set; }

        public IUnitOfWork UnitOfWork { get; set; }

        private OperatorInfo tokenModel => OperatorProvider.Get();

        private string GetRedisKey(string key) => $"{redisPrex}:{key}";

        private string GetTempSaveDir(Guid taskId, int pointIndex) => $"{TEMP_DIR}/{taskId}/{pointIndex}";

        public async Task<UploadResponse> UploadPart(PartUploadDto upload, Stream DataContent)
        {
            if (upload.Index == 0)
            {
                var projectId = await MsTaskRepository.GetAll().Where(x => x.TaskId == upload.TaskId).Select(x => x.ProjectId).FirstOrDefaultAsync();
                var projectNo = await MsProjectRepository.GetAll().Where(x => x.ProjectId == projectId).Select(x => x.ProjectNo).FirstOrDefaultAsync();
                var arr = projectNo.Split("-");
                var fileNamWithoutExt = Path.GetFileNameWithoutExtension(upload.FileName);
                var fileExt = Path.GetExtension(upload.FileName);
                var now = DateTime.Now;
                var newEntity = new MsDotCloudUpload
                {
                    TaskId = upload.TaskId,
                    FileKey = upload.Identifier,
                    BatchId = upload.BatchId,
                    //CreatedBy = tokenModel.Id,
                    FileSize = upload.TotalSize,
                    MeasureType = upload.MeasureType ?? MeasureTypeEnum.实测实量任务,
                    PointIndex = upload.PointIndex,
                    FilePath = $"{UPLOAD_DIR}/{arr[0]}/{arr[1]}/{upload.TaskId}/{fileNamWithoutExt}_{upload.PointIndex:D3}{fileExt}",
                    Begin = now,
                    CreatedTime = now,
                    TotalChunks = upload.TotalChunks,
                    Success = false,
                };
                DotCloudUploadRepository.Add(newEntity, false);
                await UnitOfWork.SaveChangesAsync();
            }
            var tempSaveDir = GetTempSaveDir(upload.TaskId, upload.PointIndex);
            if (!Directory.Exists(tempSaveDir)) Directory.CreateDirectory(tempSaveDir);
            var tempSaveFile = $"{tempSaveDir}/{upload.Identifier}-{upload.Index}";
            using var fileStream = new FileStream(tempSaveFile, FileMode.Create);
            await DataContent.CopyToAsync(fileStream);
            RedisHelper.Instance.InsertIntToList(GetRedisKey(upload.Identifier), upload.Index);

            return new UploadResponse { TemFilePath = tempSaveFile };
        }

        private async Task MergeFiles(string[] chunkFilePaths, Stream writeStream)
        {
            foreach (var filePath in chunkFilePaths)
            {
                using (var readStream = new FileStream(filePath, FileMode.Open))
                {
                    await readStream.CopyToAsync(writeStream);
                }

                File.Delete(filePath);
            }
        }

        public async Task<MergeResponse> Merge(UploadFileBaseDto dto)
        {
            var uploadEntity = await DotCloudUploadRepository.FirstOrDefaultAsync(x => x.FileKey == dto.Identifier);
            if (uploadEntity != null)
            {
                //var totalChunks = uploadEntity.TotalChunks;
                //var chachedCunks = RedisHelper.Instance.GetIntList(GetRedisKey(dto.Identifier));
                var tempSaveDir = GetTempSaveDir(uploadEntity.TaskId, uploadEntity.PointIndex);
                var chunkFiles = Directory.GetFiles(tempSaveDir);
                Array.Sort(chunkFiles, (a, b) => int.Parse(Path.GetFileNameWithoutExtension(a).Split('-')[1]).CompareTo(int.Parse(Path.GetFileNameWithoutExtension(b).Split('-')[1])));
                var file = uploadEntity.FilePath;
                var dir = Path.GetDirectoryName(file);
                if (!Directory.Exists(dir)) Directory.CreateDirectory(dir);
                using FileStream writeStrem = new FileStream(file, FileMode.Create);
                await MergeFiles(chunkFiles, writeStrem);

                //todo: CSMsTask表的逻辑
                uploadEntity.Success = true;
                uploadEntity.End = DateTime.Now;
                DotCloudUploadRepository.Update(uploadEntity, false);
                await UnitOfWork.SaveChangesAsync();

                return new MergeResponse { Url = file };
            }
            else
                throw new Exception("合并出错");
        }

        public async Task<VerifyResponse> Verify(UploadFileBaseDto dto)
        {
            var uploadEntity = await DotCloudUploadRepository.FirstOrDefaultAsync(x => x.FileKey == dto.Identifier && x.Success);

            if (uploadEntity != null)
            {
                return new VerifyResponse { NeedUpload = false, Url = uploadEntity.FilePath };
            }
            else
            {
                var chachedCunks = RedisHelper.Instance.GetIntList(GetRedisKey(dto.Identifier));
                return new VerifyResponse { NeedUpload = true, UploadedChunks = chachedCunks };
            }
        }
    }
}